﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using StateMachineViewerMobile.Runtime;
using StateMachineViewerMobile.Runtime.Xml;
using StateMachineViewerMobile.Windows.UserControls;
using System.IO;

namespace StateMachineViewerMobile
{
    public partial class Form1 : Form
    {
        private string word; // palavra que será lida no automato
        private CharEnumerator input; // enumerador para ler a palavra passo-a-passo
        private Machine machine;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.ToUI(null);
        }

        #region Menu

        /// <summary>
        /// Menu SAIR
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItem4_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        /// <summary>
        /// Menu ABRIR
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItem5_Click(object sender, EventArgs e)
        {
            try
            {
                // abrir um automato salvo
                using (OpenFileDialog f = new OpenFileDialog())
                {
                    f.Filter = "Arquivo XML|*.xml";
                    if (f.ShowDialog() == DialogResult.OK)
                    {
                        StreamReader sr = new StreamReader(f.FileName);
                        String xml = sr.ReadToEnd();

                        MachineXml machine = MachineXml.FromXml(xml);

                        this.ToUI(machine);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        
        /// <summary>
        /// Menu NOVO
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItem8_Click(object sender, EventArgs e)
        {
            this.ToUI(null);
            
            this.machineViewer1.Refresh();
        }

        /// <summary>
        /// Menu SALVAR
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItem7_Click(object sender, EventArgs e)
        {
            try
            {
                MachineXml m = this.FromUI();

                // salvar um automato
                using (SaveFileDialog f = new SaveFileDialog())
                {
                    f.Filter = "Arquivo XML|*.xml|Todos Arquivos|*.*";
                    if (f.ShowDialog() == DialogResult.OK)
                    {
                        StreamWriter tw = new StreamWriter(f.FileName);
                        tw.Write(m.ToXml());
                        tw.Flush();
                        tw.Close();

                        this.ToUI(m);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        #endregion

        #region Tab Definição

        private void txtSymbols_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtSymbols, false);
        }

        private void txtStates_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtStates, true);
        }

        private void txtInitialState_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtInitialState, true);
        }

        private void txtFinalStates_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtFinalStates, true);
        }
        
        #endregion

        #region Tab Transições

        private void txtStateIn_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtStateIn, true);
        }

        private void txtSymbol_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtSymbol, false);
        }

        private void txtStatesOut_TextChanged(object sender, EventArgs e)
        {
            ChangeCase(this.txtStatesOut, true);
        }

        private void btnAddTransaction_Click(object sender, EventArgs e)
        {
            #region Valida entradas
            if (txtStateIn.Text.Trim().Length == 0)
            {
                MessageBox.Show("Informe o estado atual.");
                return;
            }

            if (txtSymbol.Text.Trim().Length == 0)
            {
                MessageBox.Show("Informe o símbolo lido.");
                return;
            }

            if (txtStatesOut.Text.Trim().Length == 0)
            {
                MessageBox.Show("Informe o(s) próximo(s) estado(s).");
                return;
            }
            #endregion

            TransactionXml t = new TransactionXml(
                txtStateIn.Text.Trim(),
                txtSymbol.Text.Trim()[0]);
            t.StatesOut = new List<string>();

            String[] statesOut = txtStatesOut.Text.Trim().Split(',');
            foreach (String y in statesOut)
                t.StatesOut.Add(y.Trim());
            // Adicionar transição
            ListViewItem item = new ListViewItem(t.StateIn);
            item.SubItems.Add(t.Symbol.ToString());
            item.SubItems.Add(String.Join(",", t.StatesOut.ToArray()));
            item.Tag = t;
            lvwTransactions.Items.Add(item);

            txtStateIn.Text = String.Empty;
            txtSymbol.Text = String.Empty;
            txtStatesOut.Text = String.Empty;
        }

        private void btnRemoveTransacion_Click(object sender, EventArgs e)
        {
            List<TransactionXml> transactions = new List<TransactionXml>();
            // Remove a transição
            foreach (ListViewItem i in lvwTransactions.Items)
	        {
		        if (!i.Selected)
                    transactions.Add((TransactionXml)i.Tag);
	        }

            lvwTransactions.Items.Clear();
            ListViewItem item;
            foreach (TransactionXml t in transactions)
            {
                item = new ListViewItem(t.StateIn);
                item.SubItems.Add(t.Symbol.ToString());
                item.SubItems.Add(String.Join(",", t.StatesOut.ToArray()));
                item.Tag = t;
                lvwTransactions.Items.Add(item);
            }            
        }

        private void lvwTransactions_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        #endregion

        #region Tab Diagrama

        private void btnMoveNext_Click(object sender, EventArgs e)
        {
            this.MoveNextSymbol();
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            // Validar palavra
            this.RefreshMachine();

            this.machineViewer1.StartValidade(txtInput.Text.GetEnumerator());
            this.statusBar1.Text = String.Format("Entrada: {0};", txtInput.Text);
            this.word = txtInput.Text;
            this.input = txtInput.Text.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Obtém a instância da classe MachineXml a partir dos dados da interface
        /// </summary>
        /// <returns>Instância da classe MachineXml</returns>
        private MachineXml FromUI()
        {
            MachineXml machine = new MachineXml();
            machine.Name = txtName.Text;
            
            String[] states = txtStates.Text.Split(',');
            foreach (String s in states)
                machine.States.Add(s.Trim());

            String[] symbols = txtSymbols.Text.Split(',');
            foreach (String c in symbols)
            {
                String x = c.Trim();
                if (x.Length > 0)
                    machine.Symbols.Add(x[0]); // Considera somente o primeiro
            }

            // Adicionar as transicoes
            machine.Transactions.Clear();
            foreach (ListViewItem item in lvwTransactions.Items)
            {
                machine.Transactions.Add((TransactionXml)item.Tag);
            } 

            machine.InitialState = txtInitialState.Text.Trim();

            String[] finalStates = txtFinalStates.Text.Split(',');
            foreach (String y in finalStates)
                machine.FinalStates.Add(y.Trim());

            machine.IsValid();

            return machine;
        }

        /// <summary>
        /// Preenche a interface de acordo com o objeto MachineXml
        /// </summary>
        /// <param name="machine">Objeto MachineXml para carregar a interface</param>
        private void ToUI(MachineXml machineXml)
        {
            if (machineXml == null)
                machineXml = new MachineXml();

            this.txtInput.Text = String.Empty;
            this.statusBar1.Text = String.Empty;

            // Adiciona o nome da máquina
            txtName.Text = machineXml.Name;
            
            // Adiciona os estados
            txtStates.Text = String.Join(",", machineXml.States.ToArray());
            
            // Adiciona os símbolos
            List<String> symbolsAux = new List<string>();
            foreach (Char c in machineXml.Symbols)
                symbolsAux.Add(c.ToString());
            txtSymbols.Text = String.Join(",", symbolsAux.ToArray());
            
            // Adicionar as transicoes
            lvwTransactions.Items.Clear();
            ListViewItem item;
            foreach (TransactionXml t in machineXml.Transactions)
	        {
                item = new ListViewItem(t.StateIn);
                item.SubItems.Add(t.Symbol.ToString());
                item.SubItems.Add(String.Join(",", t.StatesOut.ToArray()));
                item.Tag = t;
                lvwTransactions.Items.Add(item);
            }

            // Adiciona o estado inicial
            txtInitialState.Text = machineXml.InitialState;

            // Adiciona os estados finais
            txtFinalStates.Text = String.Join(",", machineXml.FinalStates.ToArray());

            // Carrega a máquina e desenha o diagrama
            if (machineXml.Name.Length == 0)
                this.machine = null;
            else
                this.machine = Machine.FromMachineXml(machineXml);
            this.RefreshMachine();
        }

        /// <summary>
        /// Atualiza o diagrama da máquina de estados
        /// </summary>
        private void RefreshMachine()
        {
            //if (this.machine == null || this.machine.Name.Trim().Length == 0)
            //    return;

            this.machineViewer1.DrawMachine(this.machine);
        }

        /// <summary>
        /// Realiza a leitura do próximo símbolo da palavra de entrada
        /// </summary>
        private void MoveNextSymbol()
        {
            if (this.input == null)
                return;

            if (this.machine == null)
                return;

            bool hasMoreSymbols = this.machineViewer1.MoveNext();

            if (hasMoreSymbols)
            {
                if (this.input.MoveNext())
                    this.statusBar1.Text = String.Format("Entrada: {0}; Símbolo Lido: {1}", this.word, this.input.Current);
            }
            else
            {
                List<StateViewer> lastState = this.machineViewer1.GetSelectedState();

                StateViewer stateSelected =
                    lastState.Find(delegate(StateViewer vw)
                    {
                        return vw.State.IsFinal;
                    });

                // Se tem pelo menos um um estado final, então é aceito
                if (stateSelected != null)
                    this.statusBar1.Text = String.Format("A entrada \"{0}\" é válida.", this.word);
                else
                    this.statusBar1.Text = String.Format("A entrada \"{0}\" NÃO é válida.", this.word);
            }
        }

        /// <summary>
        /// Realiza a transformação do case de um TextBox
        /// </summary>
        /// <param name="txt"></param>
        /// <param name="toUpper"></param>
        private void ChangeCase(TextBox txt, bool toUpper)
        {
            int savePos = txt.SelectionStart;
            txt.Text = toUpper ? txt.Text.ToUpper() : txt.Text.ToLower();
            txt.SelectionStart = savePos;
        }

        private void btnTransicoes_Click(object sender, EventArgs e)
        {
            tabControl1.SelectedIndex = 1;
            tabPage2.Focus();
        }

    }
}